/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package TreeGenerator.Tools;

import com.jme3.asset.AssetManager;
import com.jme3.bounding.BoundingBox;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

/**
 *
 * @author Jannes
 */
public class CrossSectionGrid {
   
   //every crossSection gets a space of 128*128 pixels;
   private static int texturePartSize = 128;
    
   public static Spatial CreateCrossSectionGrid(float pixelsPerUnit,Geometry model, RenderManager renderManager, AssetManager assetManager, File textureDir, String name, int lodIndex, BoundingBox modelbound, int gridDimension){      
        Vector3f min = new Vector3f();
        Vector3f max = new Vector3f();        
        ((BoundingBox)model.getWorldBound()).getMin(min);
        ((BoundingBox)model.getWorldBound()).getMax(max);
        float xDivider = (max.x - min.x)/gridDimension;        
        float yDivider = (max.y - min.y)/gridDimension;
        float zDivider = (max.z - min.z)/gridDimension;        
        //int indexCount = currentLeafMesh.getShapeFaceCount()*3*2; //*2 for back and front faces        
        int indexCount = 3;
        ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>();
        for(int i =  0; i < gridDimension*gridDimension*gridDimension; i++){
            lists.add(new ArrayList<Integer>());
        }
        
        IndexBuffer ib = model.getMesh().getIndexBuffer();
        VertexBuffer buffer = model.getMesh().getBuffer(Type.Position);
        float[] vertexArray = BufferUtils.getFloatArray((FloatBuffer) buffer.getData()); 

        Vector3f[] minBounds = new Vector3f[gridDimension*gridDimension*gridDimension];
        for(int i = 0; i < minBounds.length; i++){
            minBounds[i] = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
        }
        Vector3f[] maxBounds = new Vector3f[gridDimension*gridDimension*gridDimension];
        for(int i = 0; i < maxBounds.length; i++){
            maxBounds[i] = new Vector3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
        }
        
        for(int i = 0; i < ib.size(); i+=indexCount){            
            float xlocation = vertexArray[ib.get(i)*3];
            float ylocation = vertexArray[ib.get(i)*3+1];
            float zlocation = vertexArray[ib.get(i)*3+2];
            int xIndex = (int)((xlocation-min.x)/xDivider);
            if(xIndex >= gridDimension){
                xIndex = gridDimension-1;
            }
            int yIndex = (int)((ylocation-min.y)/yDivider);
            if(yIndex >= gridDimension){
                yIndex = gridDimension-1;
            }
            int zIndex = (int)((zlocation-min.z)/zDivider);            
            if(zIndex >= gridDimension){
                zIndex = gridDimension-1;
            }
            int listIndex = xIndex + yIndex*gridDimension + zIndex*gridDimension*gridDimension;
            if(listIndex > lists.size()){
                JOptionPane.showMessageDialog(null, xIndex + ", " + yIndex + ", " + zIndex + ", " + listIndex);
            }
            for(int j = 0; j < indexCount; j++){
                lists.get(listIndex).add(ib.get(i+j));
                minBounds[listIndex].set(Math.min(xlocation, minBounds[listIndex].x), Math.min(ylocation, minBounds[listIndex].y), Math.min(zlocation, minBounds[listIndex].z));
                maxBounds[listIndex].set(Math.max(xlocation, maxBounds[listIndex].x), Math.max(ylocation, maxBounds[listIndex].y), Math.max(zlocation, maxBounds[listIndex].z));
            }
        }
        Geometry tempGeo;
        synchronized(model){
            tempGeo = (Geometry)model.deepClone();
            tempGeo.setName("tempGeo");
        } 
        
        File crossDir = new File(textureDir, "CrossSections");                
        //crossSections are grouped in a file with the smallest power of two dimensions;
        int numberOfSections = gridDimension*gridDimension*gridDimension;
        int tmp=1;
        while(tmp*tmp < numberOfSections){
            tmp*=2;
        }
        texturePartSize = 1024/tmp;
        BufferedImage textureImage = new BufferedImage(tmp*texturePartSize, tmp*texturePartSize, BufferedImage.TYPE_INT_ARGB);        
        
        Mesh mesh = new Mesh();
        Vector3f[] positionBuffer = new Vector3f[32*gridDimension*gridDimension*gridDimension];   
        Vector2f[] textureBuffer = new Vector2f[32*gridDimension*gridDimension*gridDimension];                           
        
        for(int i = 0; i < lists.size(); i++){
        //for(int i = 0; i < 1; i++){
            tempGeo.removeFromParent();
            ArrayList<Integer> indexList = lists.get(i);
            if(!indexList.isEmpty() && !maxBounds[i].equals(minBounds[i])){
                int[] indexes = new int[indexList.size()];
                for(int j = 0; j < indexes.length; j++){
                    indexes[j] = indexList.get(j);
                }                        
                tempGeo.getMesh().getBuffer(Type.Index).updateData(BufferUtils.createIntBuffer(indexes));                        
                model.getMesh().getBuffer(Type.Index).updateData(BufferUtils.createIntBuffer(indexes));
                VertexBuffer[] lods = new VertexBuffer[3];            
                lods[0] = new VertexBuffer(Type.Index);
                lods[0].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(indexes));        
                tempGeo.getMesh().setLodLevels(lods);                    
                tempGeo.setLodLevel(0);  
                /*
                Spatial crossSection = CrossSection.CreateCrossSection(50, tempGeo, mainPanel.getRenderManager(), assetManager, textureDir, txtSeed.getText() + "_" + mainPanel.getTexturePanel().getSeeds()+"_leafPart_" + i,2,new BoundingBox(minBounds[i], maxBounds[i]));            
                crossSection.setName("leafPart" + i);
                currentTree.attachChild(crossSection);
                */
                createMeshSection(mesh,textureImage,positionBuffer,textureBuffer,new BoundingBox(minBounds[i], maxBounds[i]),pixelsPerUnit,renderManager,model,i);
            }
        }   
        
        //saving created texture;
        File texFile = new File(crossDir,name + ".png");
        texFile.getParentFile().mkdirs();
        boolean writingDone = false;
        while(!writingDone){
            try{
                ImageIO.write(textureImage, "png", texFile);
                writingDone = true;
            }catch(Exception exc){}
        }        
        String testString = "";
        for(int i = 0; i < textureBuffer.length; i+=4){
            testString += textureBuffer[i] + "-";
            testString += textureBuffer[i+1] + ";";
            testString += textureBuffer[i+2] + "-";
            testString += textureBuffer[i+3] + "\n";
        }
        JOptionPane.showMessageDialog(null, testString);
        
        //these indexes will be replaced by the crossSectionControl
        int[] indexes = {22,20,21,21,23,22, 18,17,16,17,18,19, 10,9,8,9,10,11, 14,12,13,13,15,14, 2,0,1,1,3,2, 6,5,4,5,6,7};                                    
        mesh.setBuffer(Type.Position,3, BufferUtils.createFloatBuffer(positionBuffer));                
        mesh.setBuffer(Type.TexCoord,2, BufferUtils.createFloatBuffer(textureBuffer));                
        mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes));                                
        mesh.updateBound();
        
        VertexBuffer[] lods = new VertexBuffer[3];
        lods[0] = new VertexBuffer(Type.Index);
        lods[0].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(new int[0]));
        lods[1] = new VertexBuffer(Type.Index);
        lods[1].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(new int[0]));    
        lods[2] = new VertexBuffer(Type.Index);
        lods[2].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(indexes));    
        mesh.setLodLevels(lods);        

        Vector3f maxValues = new Vector3f();
        modelbound.getMax(maxValues);
        Vector3f minValues = new Vector3f();
        modelbound.getMin(minValues);            
        float modelWidth = maxValues.x-minValues.x;            
        float modelHeight = maxValues.y - minValues.y;                        
        float modelDepth = maxValues.z-minValues.z;
        float width = Math.max(modelWidth, modelDepth);          
        
        Geometry crossSection = new Geometry("cross" , mesh);
        String texturePath = texFile.getAbsolutePath();
        texturePath = texturePath.replace("\\","/");
        int splitPoint = texturePath.indexOf("assets/", 0) + 7;
        texturePath = texturePath.substring(splitPoint);            
        crossSection.setMaterial(createMaterial(texturePath, assetManager));
        crossSection.updateModelBound();        
        crossSection.addControl(new CrossSectionControl());
        //crossSection.move(modelbound.getCenter());
        //crossSection.move(0,-modelHeight/2,0);
        crossSection.setQueueBucket(Bucket.Transparent);
        return crossSection;        
    }  
   
    private static void createMeshSection(Mesh mesh,BufferedImage textureImage,Vector3f[] positionBuffer, Vector2f[] textureBuffer, BoundingBox modelbound, float pixelsPerUnit, RenderManager renderManager, Spatial model, int sectionIndex){
        try{
            //File crossDir = new File(textureDir, "CrossSections");            
            
            RenderToTexture rtt = new RenderToTexture(pixelsPerUnit, renderManager,model, 0, modelbound);
            BufferedImage biBack = rtt.getImage();                     
            
            rtt = new RenderToTexture(pixelsPerUnit, renderManager,model, FastMath.HALF_PI, modelbound);
            BufferedImage biRight = rtt.getImage();            
            
            rtt = new RenderToTexture(pixelsPerUnit, renderManager,model, FastMath.PI,modelbound);
            BufferedImage biFront= rtt.getImage();            
            
            rtt = new RenderToTexture(pixelsPerUnit, renderManager,model, -FastMath.HALF_PI,modelbound);
            BufferedImage biLeft = rtt.getImage();                       
            
            BoundingBox bb = modelbound;            
            //int maxWidth = Math.max(biFront.getWidth()+Math.abs((int)(center.x*pixelsPerUnit)), biLeft.getWidth()+Math.abs((int)(center.z*pixelsPerUnit)));
            int maxWidth = Math.max(biFront.getWidth(), biLeft.getWidth());                        
            BufferedImage img = new BufferedImage(maxWidth*2,biFront.getHeight()*2,BufferedImage.TYPE_INT_ARGB);
            Graphics g = img.createGraphics();            
            if(biFront.getWidth() > biLeft.getWidth()){  
                g.drawImage(biFront,0, 0,biFront.getWidth(),biFront.getHeight(), null);
                g.drawImage(biBack,0,biFront.getHeight(),biBack.getWidth(),biBack.getHeight(), null);                
                int half_diff = (biFront.getWidth()-biLeft.getWidth())/2;                
                g.drawImage(biLeft,maxWidth+half_diff,0,biLeft.getWidth(),biLeft.getHeight(),null);
                g.drawImage(biRight,maxWidth+half_diff,biFront.getHeight(),biRight.getWidth(),biRight.getHeight(),null);                                
            }else{
                int half_diff = (biLeft.getWidth()-biFront.getWidth())/2;
                g.drawImage(biFront,half_diff, 0,biFront.getWidth(),biFront.getHeight(), null);
                g.drawImage(biBack,half_diff,biFront.getHeight(),biBack.getWidth(),biBack.getHeight(), null);                                
                g.drawImage(biLeft,maxWidth,0,biLeft.getWidth(),biLeft.getHeight(),null);
                g.drawImage(biRight,maxWidth,biFront.getHeight(),biRight.getWidth(),biRight.getHeight(),null);
            }            
            g.dispose();            
            g = textureImage.createGraphics();
            int x_position = (sectionIndex * texturePartSize ) % textureImage.getWidth();
            int y_position = ((sectionIndex * texturePartSize ) / textureImage.getWidth())*texturePartSize;            
            g.drawImage(img, x_position, y_position, texturePartSize, texturePartSize, null);
            g.dispose();            
            
            Vector3f maxValues = new Vector3f();
            bb.getMax(maxValues);
            Vector3f minValues = new Vector3f();
            bb.getMin(minValues);            
            float modelWidth = maxValues.x-minValues.x;            
            float modelHeight = maxValues.y - minValues.y;                        
            float modelDepth = maxValues.z-minValues.z;
            float width = Math.max(modelWidth, modelDepth);
            float xOffset = minValues.x;
            float yOffset = minValues.y;
            float zOffset = minValues.z;
            int bufferOffset = sectionIndex*32;
            
            //texture: F L 
            //         B R                        
            
            //TODO positionVectors: move them to the correct possition
            
            maxWidth = texturePartSize/2;                                            
            
            //right-part right quad
            positionBuffer[0+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);            
            positionBuffer[1+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,width/2+zOffset);
            positionBuffer[2+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);
            positionBuffer[3+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,width/2+zOffset);       
            float xStart = ((float)(maxWidth*1.5+x_position))/textureImage.getWidth();
            float xEnd = ((float)(maxWidth*2)+x_position)/textureImage.getWidth();
            float yStart = ((float)y_position+maxWidth)/textureImage.getHeight();
            float yEnd = ((float)(y_position+texturePartSize))/textureImage.getHeight();
            textureBuffer[0+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[1+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[2+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[3+bufferOffset] = new Vector2f(xEnd,yEnd);
                        
            //left-part left quad
            positionBuffer[4+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[5+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,width/2+zOffset);
            positionBuffer[6+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);
            positionBuffer[7+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,width/2+zOffset);           
            xStart = ((float)(maxWidth*1.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth+x_position))/textureImage.getWidth();
            yStart = ((float)y_position)/textureImage.getHeight();
            yEnd = ((float)(y_position+maxWidth))/textureImage.getHeight();
            textureBuffer[4+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[5+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[6+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[7+bufferOffset] = new Vector2f(xEnd,yEnd);           
            
            //left-part back quad                                  
            positionBuffer[8+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[9+bufferOffset] = new Vector3f(-width/2+xOffset,0+yOffset,0+zOffset);
            positionBuffer[10+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);            
            positionBuffer[11+bufferOffset] = new Vector3f(-width/2+xOffset,modelHeight+yOffset,0+zOffset);
            xStart = ((float)(maxWidth*0.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth*0+x_position))/textureImage.getWidth();
            yStart = ((float)y_position+maxWidth)/textureImage.getHeight();
            yEnd = ((float)(y_position+texturePartSize))/textureImage.getHeight();
            textureBuffer[8+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[9+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[10+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[11+bufferOffset] = new Vector2f(xEnd,yEnd);
            
            //right-part back quad
            positionBuffer[12+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[13+bufferOffset] = new Vector3f(width/2+xOffset,0+yOffset,0+zOffset);
            positionBuffer[14+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);            
            positionBuffer[15+bufferOffset] = new Vector3f(width/2+xOffset,modelHeight+yOffset,0+zOffset);
            xStart = ((float)(maxWidth*0.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth*1.0+x_position))/textureImage.getWidth();
            yStart = ((float)y_position+maxWidth)/textureImage.getHeight();
            yEnd = ((float)(y_position+texturePartSize))/textureImage.getHeight();
            textureBuffer[12+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[13+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[14+bufferOffset] = new Vector2f(xStart,yEnd);            
            textureBuffer[15+bufferOffset] = new Vector2f(xEnd,yEnd);  
            
            //left-part right quad 
            positionBuffer[16+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[17+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,-width/2+zOffset);
            positionBuffer[18+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);
            positionBuffer[19+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,-width/2+zOffset);
            xStart = ((float)(maxWidth*0.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(x_position))/textureImage.getWidth();
            yStart = ((float)y_position+maxWidth)/textureImage.getHeight();
            yEnd = ((float)(y_position+texturePartSize))/textureImage.getHeight();
            textureBuffer[16+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[17+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[18+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[19+bufferOffset] = new Vector2f(xEnd,yEnd);
            
            //right-part left quad
            positionBuffer[20+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[21+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,-width/2+zOffset);
            positionBuffer[22+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);
            positionBuffer[23+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,-width/2+zOffset);   
            xStart = ((float)(maxWidth*1.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth*2+x_position))/textureImage.getWidth();
            yStart = ((float)y_position)/textureImage.getHeight();
            yEnd = ((float)(y_position+maxWidth))/textureImage.getHeight();
            textureBuffer[20+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[21+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[22+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[23+bufferOffset] = new Vector2f(xEnd,yEnd);                        
            
            //right-part front quad
            positionBuffer[24+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[25+bufferOffset] = new Vector3f(-width/2+xOffset,0+yOffset,0+zOffset);
            positionBuffer[26+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);            
            positionBuffer[27+bufferOffset] = new Vector3f(-width/2+xOffset,modelHeight+yOffset,0+zOffset);
            xStart = ((float)(maxWidth*0.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth*1)+x_position)/textureImage.getWidth();
            yStart = ((float)y_position)/textureImage.getHeight();
            yEnd = ((float)(y_position+maxWidth))/textureImage.getHeight();
            textureBuffer[24+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[25+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[26+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[27+bufferOffset] = new Vector2f(xEnd,yEnd);
            
            //left-part front quad
            positionBuffer[28+bufferOffset] = new Vector3f(0+xOffset,0+yOffset,0+zOffset);
            positionBuffer[29+bufferOffset] = new Vector3f(width/2+xOffset,0+yOffset,0+zOffset);
            positionBuffer[30+bufferOffset] = new Vector3f(0+xOffset,modelHeight+yOffset,0+zOffset);            
            positionBuffer[31+bufferOffset] = new Vector3f(width/2+xOffset,modelHeight+yOffset,0+zOffset);
            xStart = ((float)(maxWidth*0.5+x_position))/textureImage.getWidth();
            xEnd = ((float)(maxWidth*0+x_position))/textureImage.getWidth();
            yStart = ((float)y_position)/textureImage.getHeight();
            yEnd = ((float)(y_position+maxWidth))/textureImage.getHeight();
            textureBuffer[28+bufferOffset] = new Vector2f(xStart,yStart);
            textureBuffer[29+bufferOffset] = new Vector2f(xEnd,yStart);
            textureBuffer[30+bufferOffset] = new Vector2f(xStart,yEnd);
            textureBuffer[31+bufferOffset] = new Vector2f(xEnd,yEnd);                       
            
        }catch(Exception exc){
            System.out.println("creating crossSectionGrid failed:" + exc.getMessage());
        }
    }            
    
    private static Material createMaterial(String texturePath,AssetManager assetManager){
        Material mat = null;        
        mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");                                 
        Texture texture = assetManager.loadTexture(texturePath);
        mat.setTexture("ColorMap", texture);
        mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);        
        mat.getAdditionalRenderState().setDepthWrite(false);
        return mat;
    }
}
